11. Migration to Java 9 Modules

Migration to Java 9 Modules

ND079 JPND C3 L3 A09 Mitigation To Java 9 Modules V3

Should You Upgrade

You don't need to upgrade to modules just to use a newer version of Java. All versions work without a module-info.java.

When to **Avoid **Modules:

  • Many Legacy Dependencies
  • Infrequent Change
  • Not Shared

When to **Use **Modules:

  • New Projects
  • Application Used By Other Applications
  • Hide Internal Methods and Data Types

Preparing To Upgrade

  1. Review Test Coverage
  2. Upgrade Tooling
    • IntelliJ 2018.2+
    • Maven 3.5.0+
    • maven-compiler-plugin 3.8.0+
    • maven-surefire-plugin 2.22.0+
  3. Update Dependencies

Update Language Level

Download the target JDK and update the pom.xml. For example:

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>14</maven.compiler.source>
    <maven.compiler.target>14</maven.compiler.target>
</properties>

Resolve Duplicate Packages

Placing multiple projects with the same package on the classpath is not a problem, because the packages simply combine. You cannot place packages with the same name on the Module path, however. If you have different projects that share a package name, you will need to refactor one of them to use a different package name.

You Cannot Place Duplicate Packages On Modulepath

You Cannot Place Duplicate Packages On Modulepath

Create module-info.java

  1. Create a minimal module-info.java in the src/main/java directory containing only your new module name.
  2. Resolve errors in IntelliJ. You should be able to mouseover or use the "quick fix" key bind to automatically add missing requires, exports, and opens statements.
  3. Build the project with maven using mvn package

What Can Go Wrong?

ND079 JPND C3 L3 A10 What Can Go Wrong Migration To Java 9 Modules

Common Problems

Most of the problems with Modules revolve around the relationship between dependencies and transitive dependencies in which one or both are non-modular projects.

Dependencies Use Reflection

If dependencies access your class via reflection, you may not notice until you run the project.

Unable to make public com.udacity.jpnd.Foo() accessible: module com.udacity.jpnd does not "exports com.udacity.jpnd" to module com.other.module
  • Module ‘com.other.module’ is trying to access ‘com.udacity.jpnd’.
  • Resolve by exporting or opening the required package.

Modular Transitive Dependencies of non-modular Dependencies

If your non-modular dependencies reference transitive dependencies that are modular, those dependencies will become modules and be inaccessible to your module unless you explicitely require them.

Using `requires piston` to Access Modular Transitive Dependency

Using requires piston to Access Modular Transitive Dependency

This issue may not be apparent until you run the program and you receive a NoClassDefFoundError when the engine module tries to execute some method from piston and can't find it.

You may encounter this error in the final project when including Amazon SDK dependencies:

https://github.com/aws/aws-sdk-java-v2/issues/1869

Reflection Access to Non-Modules

You cannot open your module to the Unnamed Module. If a transitive dependency requires reflection access to your module, it will be unable to access that package unless you fully open the package to everyone.

This occurs in some Test frameworks that use reflection libraries as transitive dependencies. One solution for this case is to limit the scope in which you open your package by only opening the module during the execution of the testing plugin.

        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>3.0.0-M5</version>
          <configuration>
            <argLine>
              --add-opens com.udacity.jpnd.mymodule/com.udacity.jpnd.mypackage=ALL-UNNAMED
            </argLine>
          </configuration>
        </plugin>

Libraries Removed from JDK

Some libraries are not exposed by the core JDK as modules. If you relied on one of the JDK internal libraries in the javax or com.sun packages, you may encounter ClassNotFoundException trying to access those classes. You can check for these classes using:

jdeps -jdkinternals pathToClasses

Migration Guides

It's hard to address all possible issues, so definitely consult some migration guides such as:

https://docs.oracle.com/en/java/javase/11/migrate/index.html